<template>
  <div class="tree-transfer">
    <div class="tree-box">
      <el-tree
        ref="leftTree"
        :data="leftData"
        node-key="id"
        show-checkbox
        :props="defaultProps"
        @check="handleLeftCheck"
      />
    </div>
    
    <div class="transfer-buttons">
      <el-button @click="moveToRight" :disabled="!checkedLeft.length">→</el-button>
      <el-button @click="moveToLeft" :disabled="!checkedRight.length">←</el-button>
    </div>
    
    <div class="tree-box">
      <el-tree
        ref="rightTree"
        :data="rightData"
        node-key="id"
        show-checkbox
        :props="defaultProps"
        @check="handleRightCheck"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import type { Node } from 'element-plus/es/components/tree'

interface TreeNode {
  id: string | number
  label: string
  children?: TreeNode[]
  // 可以添加其他自定义属性
  [key: string]: any
}

const props = defineProps<{
  data: TreeNode[] // 原始数据
  modelValue: TreeNode[] // 选中的数据
  defaultProps?: { // 树形配置
    children?: string
    label?: string
  }
}>()

const emit = defineEmits(['update:modelValue'])

const defaultProps = ref({
  children: 'children',
  label: 'label',
  ...props.defaultProps
})

// 左右侧数据
const leftData = ref<TreeNode[]>([])
const rightData = ref<TreeNode[]>([])

// 选中的节点
const checkedLeft = ref<(string | number)[]>([])
const checkedRight = ref<(string | number)[]>([])

// 初始化数据
const initData = () => {
  leftData.value = cloneDeep(props.data)
  rightData.value = cloneDeep(props.modelValue)
}

// 处理左侧选中
const handleLeftCheck = (data: TreeNode, { checkedKeys }: { checkedKeys: (string | number)[] }) => {
  checkedLeft.value = checkedKeys
}

// 处理右侧选中
const handleRightCheck = (data: TreeNode, { checkedKeys }: { checkedKeys: (string | number)[] }) => {
  checkedRight.value = checkedKeys
}

// 移动到右侧
const moveToRight = () => {
  const nodesToMove = findNodesByIds(leftData.value, checkedLeft.value)
  rightData.value = [...rightData.value, ...nodesToMove]
  leftData.value = removeNodes(leftData.value, checkedLeft.value)
  updateModelValue()
}

// 移动到左侧
const moveToLeft = () => {
  const nodesToMove = findNodesByIds(rightData.value, checkedRight.value)
  leftData.value = [...leftData.value, ...nodesToMove]
  rightData.value = removeNodes(rightData.value, checkedRight.value)
  updateModelValue()
}

// 辅助方法：根据ID查找节点
const findNodesByIds = (data: TreeNode[], ids: (string | number)[]): TreeNode[] => {
  const result: TreeNode[] = []
  const find = (nodes: TreeNode[]) => {
    nodes.forEach(node => {
      if (ids.includes(node.id)) {
        result.push({ ...node, children: undefined }) // 移除children防止引用
      }
      if (node.children) find(node.children)
    })
  }
  find(data)
  return result
}

// 辅助方法：移除节点
const removeNodes = (data: TreeNode[], ids: (string | number)[]): TreeNode[] => {
  return data.filter(node => {
    if (ids.includes(node.id)) return false
    if (node.children) {
      node.children = removeNodes(node.children, ids)
    }
    return true
  })
}

// 更新模型值
const updateModelValue = () => {
  emit('update:modelValue', cloneDeep(rightData.value))
}

// 初始化
initData()
watch(() => props.data, initData)
</script>

<style scoped>
.tree-transfer {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.tree-box {
  width: 45%;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  padding: 10px;
  height: 400px;
  overflow: auto;
}

.transfer-buttons {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 0 20px;
}

.el-tree {
  background: transparent;
}
</style>